package com.gxa.modules.shiro.global;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gxa.common.constants.Constants;
import com.gxa.modules.entity.SysBtn;
import com.gxa.modules.entity.SysMenu;
import com.gxa.modules.entity.SysRole;
import com.gxa.modules.entity.Employee;
import com.gxa.modules.mapper.SysMenuMapper;
import com.gxa.modules.mapper.SysRoleMapper;
import com.gxa.modules.mapper.EmployeeMapper;
import com.gxa.modules.shiro.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义Realm
 */
@Slf4j
public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof AuthenticationToken;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //根据账户名查询系统用户信息
        String userName =  String.valueOf(principalCollection.getPrimaryPrincipal());
        // 根据用户名获取数据库中的用户信息
        QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("employee_name",userName);
        Employee sys_user = employeeMapper.selectOne(queryWrapper);
        List<SysRole> sysRoleList = sysRoleMapper.findSysRoleByUserId(sys_user.getEmployeeId());
        if(sysRoleList.size() > 0){
            //存放角色Id，可能会有多个
            List<Integer> role_ids = new ArrayList<>();
            //遍历去重
            Set<String> roleSets = new HashSet<>();
            for (SysRole r:sysRoleList) {
                roleSets.add(r.getTitle());
                //将每一次循环的角色ID存放到集合中
                role_ids.add(r.getId());
            }
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //授权-授角色
            info.setRoles(roleSets);
            //根据角色id查看对应的权限
            List<SysMenu> sysMenuList = sysMenuMapper.findMenuListByRoleIds(role_ids);
            if(sysMenuList.size() > 0){
                //遍历去重
                Set<String> menuSets = new HashSet<>();
                for (SysMenu sm:sysMenuList) {
                    menuSets.add(sm.getMenuName());
                }
                //授权，授对应的权限
                info.setStringPermissions(menuSets);
            }
            return info;
        }
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
        String username = authenticationToken.getPrincipal().toString();
        log.info("{}系统用户进入认证方法",username);
        // 根据用户名获取数据库中的用户信息
        QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("employee_account",username);
        Employee sys_user = employeeMapper.selectOne(queryWrapper);
        if(sys_user == null){
            throw new UnknownAccountException();
        }
        if(sys_user.getDepartureStatus() == Constants.ACCOUNT_LOCK){//冻结
            throw new LockedAccountException();
        }
        /* 传入密码自动判断是否正确
         * 参数1：传入对象给Principal
         * 参数2：正确的用户密码
         * 参数3：加盐处理
         * 参数4：固定写法
         */
        // 获取数据库中对应账户的盐值 == 对盐进行加密处理
        ByteSource salt = ByteSource.Util.bytes(sys_user.getSalt());
        log.info("查询系统用户进行密码验证匹配，密码：{}, 盐值：{}",sys_user.getEmployeePassword(),salt);
        //数据库中的密码 == 客户端传入的密码进行比较 --- 交给shiro去做
        return new SimpleAuthenticationInfo(sys_user, sys_user.getEmployeePassword(), salt, getName());
    }

    /**
     * 自定义密码验证匹配器
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        setCredentialsMatcher(new SimpleCredentialsMatcher() {
            @Override
            public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                SimpleAuthenticationInfo info = (SimpleAuthenticationInfo) authenticationInfo;
                // 获取明文密码及密码盐
                String password = String.valueOf(token.getPassword());

                String salt = CodecSupport.toString(info.getCredentialsSalt().getBytes());
                System.out.println(ShiroUtil.encrypt(password, salt));
                return equals(ShiroUtil.encrypt(password, salt), info.getCredentials());
            }
        });
    }
}
